/*
 * Copyright 2010-2025 JetBrains s.r.o. and Kotlin Programming Language contributors.
 * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
 */

package org.jetbrains.kotlin.analysis.decompiler.psi

import com.intellij.psi.PsiErrorElement
import org.jetbrains.kotlin.analysis.decompiler.psi.file.KtDecompiledFile
import org.jetbrains.kotlin.analysis.stubs.AbstractCompiledStubsTest
import org.jetbrains.kotlin.analysis.test.framework.base.AbstractAnalysisApiBasedTest
import org.jetbrains.kotlin.analysis.test.framework.projectStructure.KtTestModule
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisApiTestConfigurator
import org.jetbrains.kotlin.analysis.utils.errors.requireIsInstance
import org.jetbrains.kotlin.analysis.utils.printer.prettyPrint
import org.jetbrains.kotlin.platform.TargetPlatform
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtTreeVisitorVoid
import org.jetbrains.kotlin.test.Assertions
import org.jetbrains.kotlin.test.services.TestServices
import org.jetbrains.kotlin.test.services.assertions

/**
 * This test is supposed to validate a decompiler text output
 *
 * @see org.jetbrains.kotlin.analysis.stubs.AbstractCompiledStubsTest
 */
abstract class AbstractDecompiledTextTest(defaultTargetPlatform: TargetPlatform) : AbstractAnalysisApiBasedTest() {
    override val configurator: AnalysisApiTestConfigurator = AbstractCompiledStubsTest.CompiledStubsTestConfigurator(defaultTargetPlatform)

    override fun doTestByMainModuleAndOptionalMainFile(mainFile: KtFile?, mainModule: KtTestModule, testServices: TestServices) {
        val files = mainModule.ktFiles.sortedBy(KtFile::getName)

        val actual = prettyPrint {
            if (files.isEmpty()) {
                appendLine("NO FILES")
                return@prettyPrint
            }

            val file = files.singleOrNull()
            if (file != null) {
                append(file.text)
            } else {
                printCollection(files, separator = "\n") { file ->
                    appendLine("${file.name}:")
                    withIndent {
                        append(file.text)
                    }
                }
            }
        }

        testServices.assertions.assertEqualsToTestOutputFile(actual, extension = ".decompiledText.txt")

        for (file in files) {
            requireIsInstance<KtDecompiledFile>(file)
            file.validateTree(testServices.assertions)
        }
    }

    companion object {
        fun KtDecompiledFile.validateTree(assertions: Assertions) {
            val visitor = object : KtTreeVisitorVoid() {
                override fun visitErrorElement(element: PsiErrorElement) {
                    assertions.fail {
                        val parent = element.parent
                        """
                            Decompiled file should not contain syntax errors!
                            Parent class: ${parent::class.simpleName}
                            Parent text: ${parent.text}
                        """.trimIndent()
                    }
                }
            }

            accept(visitor)
        }
    }
}
